﻿#include "encode.h"

void encode::ConvertBinToQR(int imageWidth, int imageHeight, int blockSize, int fps,
    const string& inputBinPath, const string& outputVideoPath, int videoLengthMs) {

    // 打开输入文件
    FILE* binFp = NULL;
    errno_t openErr = fopen_s(&binFp, inputBinPath.c_str(), "rb");
    if (openErr != 0) {
        cout << "错误: 无法打开输入文件: " << inputBinPath << endl;
        return;
    }

    // 创建临时二进制转换文件
    string tempBinPath = "temp_qr_data.txt";
    FILE* tempFp = NULL;
    errno_t createErr = fopen_s(&tempFp, tempBinPath.c_str(), "w");
    if (createErr != 0) {
        cout << "错误: 无法创建临时文件" << endl;
        fclose(binFp);
        return;
    }

    TransBin(binFp, tempFp);
    cout << "BIN数据已转换为二维码准备数据" << endl;

    // 生成二维码图像
    vector<string> qrPaths = DrawQRImages(imageHeight, imageWidth, blockSize, tempBinPath, fps, videoLengthMs);

    // 检查二维码数量是否会导致视频超长
    int expectedFrames = qrPaths.size() + 1; // 起始帧 + 所有二维码帧
    int expectedDurationMs = (expectedFrames * 1000) / fps;

    if (expectedDurationMs > videoLengthMs) {
        cout << "警告: 编码数据量过大，预计视频长度 " << expectedDurationMs << "ms 超过约束 " << videoLengthMs << "ms" << endl;
        cout << "请调整参数或减少输入数据量" << endl;

        // 清理临时文件
        remove(tempBinPath.c_str());
        return;
    }

    // 生成视频
    if (!qrPaths.empty()) {
        if (CreateVideoFromQRCodes(qrPaths, fps, outputVideoPath, imageWidth, imageHeight, blockSize, videoLengthMs)) {
            cout << "视频创建成功: " << outputVideoPath << endl;
        }
        else {
            cout << "错误: 创建视频失败" << endl;
        }
    }

    // 清理临时文件
    remove(tempBinPath.c_str());
}
void encode::TransBin(FILE* readFp, FILE* writeFp) {
    if (!readFp || !writeFp) return;

    fseek(readFp, 0, SEEK_END);
    long totalSize = ftell(readFp);
    fseek(readFp, 0, SEEK_SET);

    const int FILL_CODE[2] = { 237, 17 };
    unsigned char* buf = new unsigned char[18];
    long readBytes = 0;

    while (readBytes < totalSize) {
        size_t readCnt = fread(buf, sizeof(unsigned char), 18, readFp);
        readBytes += readCnt;

        if (readCnt > 0) {
            int dataBlock[20] = { 0 };
            for (int i = 0; i < readCnt; i++) {
                dataBlock[i] = buf[i];
            }

            if (readCnt < 18) {
                for (int k = readCnt; k < 18; k++) {
                    dataBlock[k] = FILL_CODE[(k - readCnt) % 2];
                }
            }

            unsigned short checkSum = chksum(dataBlock, 18);
            dataBlock[18] = checkSum >> 8;
            dataBlock[19] = checkSum & 0xFF;

            for (int k = 0; k < 20; k++) {
                char binStr[9];
                for (int b = 7; b >= 0; b--) {
                    binStr[7 - b] = ((dataBlock[k] >> b) & 1) ? '1' : '0';
                }
                binStr[8] = '\0';
                fprintf(writeFp, "%s", binStr);
            }
        }
    }

    delete[] buf;
    fclose(readFp);
    fclose(writeFp);
}
int encode::chksum(int data[], unsigned short len) {
    unsigned long acc = 0;
    unsigned short src;
    int i = 0;
    while (i < len) {
        if (i + 1 < len) {
            src = (data[i] << 8) | data[i + 1];
            acc += src;
            i += 2;
        }
        else {
            src = data[i] << 8;
            acc += src;
            i += 1;
        }
        // 处理进位
        if ((acc & 0xFFFF0000UL) != 0) {
            acc = (acc >> 16) + (acc & 0x0000FFFFUL);
            if ((acc & 0xFFFF0000UL) != 0) {
                acc = (acc >> 16) + (acc & 0x0000FFFFUL);
            }
        }
    }
    unsigned short result = ~((unsigned short)acc);
    return result;
}
void encode::Draw_Template(Mat img, int col, int row, int blockSize) {
    // 先绘制保护白边（2 block宽度）
    for (int i = 0; i < col; i++) {
        // 上边界的2行
        Draw_Color_block(img, 0, i, blockSize, 1); // 白色
        Draw_Color_block(img, 1, i, blockSize, 1); // 白色
        // 下边界的2行
        Draw_Color_block(img, row - 2, i, blockSize, 1); // 白色
        Draw_Color_block(img, row - 1, i, blockSize, 1); // 白色
    }
    for (int i = 0; i < row; i++) {
        // 左边界的2列
        Draw_Color_block(img, i, 0, blockSize, 1); // 白色
        Draw_Color_block(img, i, 1, blockSize, 1); // 白色
        // 右边界的2列
        Draw_Color_block(img, i, col - 2, blockSize, 1); // 白色
        Draw_Color_block(img, i, col - 1, blockSize, 1); // 白色
    }

    // 定位符位置
    vector<Point> positions = {
        Point(8.5, 8.5),
        Point(col - 8.5, 8.5),
        Point(8.5, row - 8.5)
    };

    // 绘制每个定位符
    for (const auto& center : positions) {
        int x = center.x;
        int y = center.y;
        // 1. 18x18白色外框
        for (int i = -8.5; i <= 8.5; i++) {
            for (int j = -8.5; j <= 8.5; j++) {
                Draw_Color_block(img, y + i, x + j, blockSize, 1); // 白色
            }
        }
        // 2. 14x14黑色外框
        for (int i = -6.5; i <= 6.5; i++) {
            for (int j = -6.5; j <= 6.5; j++) {
                Draw_Color_block(img, y + i, x + j, blockSize, 0); // 黑色
            }
        }
        // 3. 10x10白色中间层
        for (int i = -4.5; i <= 4.5; i++) {
            for (int j = -4.5; j <= 4.5; j++) {
                Draw_Color_block(img, y + i, x + j, blockSize, 1); // 白色
            }
        }
        // 3. 6x6黑色内层
        for (int i = -2.5; i <= 2.5; i++) {
            for (int j = -2.5; j <= 2.5; j++) {
                Draw_Color_block(img, y + i, x + j, blockSize, 0); // 黑色
            }
        }
    }

 int rightBottomX = col - 6; // 调整x坐标，确保不越界
    int rightBottomY = row - 6; // 调整y坐标，确保不越界
    
    // 1. 9x9白色外框
    for (int i = -4; i <= 4; i++) {
        for (int j = -4; j <= 4; j++) { // 修正循环范围
            if (rightBottomY + i >= 0 && rightBottomY + i < row && 
                rightBottomX + j >= 0 && rightBottomX + j < col) {
                Draw_Color_block(img, rightBottomY + i, rightBottomX + j, blockSize, 1);
            }
        }
    }
    
    // 2. 7x7黑色外框
    for (int i = -3; i <= 3; i++) {
        for (int j = -3; j <= 3; j++) {
            if (rightBottomY + i >= 0 && rightBottomY + i < row && 
                rightBottomX + j >= 0 && rightBottomX + j < col) {
                Draw_Color_block(img, rightBottomY + i, rightBottomX + j, blockSize, 0);
            }
        }
    }
    
    // 3. 5x5白色中间层
    for (int i = -2; i <= 2; i++) {
        for (int j = -2; j <= 2; j++) {
            if (rightBottomY + i >= 0 && rightBottomY + i < row && 
                rightBottomX + j >= 0 && rightBottomX + j < col) {
                Draw_Color_block(img, rightBottomY + i, rightBottomX + j, blockSize, 1);
            }
        }
    }
    
    // 4. 3x3黑色内层
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            if (rightBottomY + i >= 0 && rightBottomY + i < row && 
                rightBottomX + j >= 0 && rightBottomX + j < col) {
                Draw_Color_block(img, rightBottomY + i, rightBottomX + j, blockSize, 0);
            }
        }
    }

}
void encode::Draw_White_block(Mat img, int pt0, int pt1, int blockSize) {
    rectangle(img,
        Point(pt1 * blockSize + 0.5, pt0 * blockSize + 0.5),
        Point((pt1 + 1) * blockSize - 0.5, (pt0 + 1) * blockSize - 0.5),
        Scalar(255, 255, 255),
        FILLED, LINE_8, 0);
}
void encode::Draw_Color_block(Mat img, int pt0, int pt1, int blockSize, int colorIndex) {
    if (colorIndex < 0 || colorIndex >= 8) {
        colorIndex = 0;
    }

    rectangle(img,
        Point(pt1 * blockSize + 0.5, pt0 * blockSize + 0.5),
        Point((pt1 + 1) * blockSize - 0.5, (pt0 + 1) * blockSize - 0.5),
        COLORS[colorIndex],
        FILLED, LINE_8, 0);
}
vector<string> encode::DrawQRImages(int length, int width, int blockSize, const string& binFilePath, int fps, int videoLengthMs) {
    vector<string> qrPaths;
    const unsigned char FILL_BIN[] = "11101101000100011110110100010001";
    FILE* binFp = NULL;
    errno_t openErr = fopen_s(&binFp, binFilePath.c_str(), "r");

    if (openErr != 0) {
        cout << "Error: Can't read binary file! Path: " << binFilePath << ", Error code: " << openErr << endl;
        return qrPaths;
    }

    fseek(binFp, 0, SEEK_END);
    long totalBinLen = ftell(binFp);
    if (totalBinLen <= 0) {
        cout << "Error: Binary file is empty! Path: " << binFilePath << endl;
        fclose(binFp);
        return qrPaths;
    }
    fseek(binFp, 0, SEEK_SET);

    // 计算最大允许的帧数（包括起始帧）
    int maxTotalFrames = (videoLengthMs * fps) / 1000;
    int maxDataFrames = maxTotalFrames - 1; // 减去起始帧

    if (maxDataFrames <= 0) {
        cout << "Error: Video length too short, no space for data frames" << endl;
        fclose(binFp);
        return qrPaths;
    }

    cout << "视频长度限制: " << videoLengthMs << "ms, FPS: " << fps << endl;
    cout << "最大总帧数: " << maxTotalFrames << ", 最大数据帧数: " << maxDataFrames << endl;

    int qrCount = 0;
    long currBinLoc = 0;
    int col = width / blockSize;
    int row = length / blockSize;

    int positionBlocks = 15 * 15 * 3 + 8 * 8;
    int availableBlocks = (col - 4) * (row - 4) - positionBlocks;
    int singleQrCap = availableBlocks * 2; // 每个二维码的容量（比特）

    srand(static_cast<unsigned int>(time(nullptr)));

    while (currBinLoc < totalBinLen && qrCount < maxDataFrames) {
        int remainingBits = totalBinLen - currBinLoc;
        int currCap = (remainingBits > singleQrCap) ? singleQrCap : remainingBits;
        currCap = (currCap / 2) * 2; // 确保是2的倍数

        if (currCap == 0) {
            break;
        }

        Mat qrImg = Mat::zeros(length, width, CV_8UC3);
        Draw_Template(qrImg, col, row, blockSize);

        // 编码容量信息（16位）
        unsigned char lenBin[16] = { '0' };
        int tempCap = currCap;
        int lenIdx = 15;
        while (tempCap > 0 && lenIdx >= 0) {
            lenBin[lenIdx] = (tempCap % 2) + '0';
            tempCap /= 2;
            lenIdx--;
        }
        for (int k = 0; k < 16; k++) {
            if (lenBin[k] == '1') {
                Draw_White_block(qrImg, 2, k + 17, blockSize);
            }
        }

        bool isEnd = false;
        int blockRow = 2;
        int blockCol = 2;
        int fillIdx = 0;
        int filledBits = 0;
        int filledBlocks = 0;

        auto isInPositionArea = [&](int r, int c) {
            const int POS_SIZE = 15;
            bool inTopLeft = (r >= 2 && r < 2 + POS_SIZE && c >= 2 && c < 2 + POS_SIZE);
            bool inTopRight = (r >= 2 && r < 2 + POS_SIZE && c >= col - 2 - POS_SIZE && c < col - 2);
            bool inBottomLeft = (r >= row - 2 - POS_SIZE && r < row - 2 && c >= 2 && c < 2 + POS_SIZE);
            bool inBottomRight = (r >= row - 10 && r < row - 2 && c >= col - 10 && c < col - 2);
            bool inFinder = inTopLeft || inTopRight || inBottomLeft || inBottomRight;
            bool inTopSyncOrLen = (r == 2) && (c >= 17 && c <= 32);
            bool inSafeArea = (r == 0) || (r == row - 1) || (c == 0) || (c == col - 1) ||
                (r == 1) || (r == row - 2) || (c == 1) || (c == col - 2);
            return inFinder || inTopSyncOrLen || inSafeArea;
            };

        while (!isEnd) {
            while (isInPositionArea(blockRow, blockCol)) {
                if (blockRow % 2 == 1) {
                    if (blockCol > 0) {
                        blockCol--;
                    }
                    else {
                        blockRow++;
                    }
                }
                else {
                    if (blockCol < col - 2) {
                        blockCol++;
                    }
                    else {
                        blockRow++;
                    }
                }
                if (blockRow >= row - 2) {
                    isEnd = true;
                    break;
                }
            }
            if (isEnd) break;

            int colorIndex = 0;
            bool dataAvailable = true;

            // 检查是否是最后一张二维码且需要随机填充
            bool isLastQR = (currBinLoc + singleQrCap >= totalBinLen) || (qrCount == maxDataFrames - 1);
            if (isLastQR && filledBits >= currCap) {
                // 生成随机颜色索引（0-3）
                colorIndex = rand() % 4;
            }
            else {
                for (int i = 0; i < 2; i++) {
                    unsigned char binChar;
                    if (currBinLoc < totalBinLen && filledBits < currCap) {
                        binChar = fgetc(binFp);
                        currBinLoc++;
                        filledBits++;
                        if (binChar != '0' && binChar != '1') {
                            dataAvailable = false;
                            break;
                        }
                    }
                    else {
                        binChar = FILL_BIN[fillIdx % 2];
                        fillIdx++;
                        filledBits++;
                    }
                    colorIndex = (colorIndex << 1) | (binChar == '1' ? 1 : 0);
                }

                if (!dataAvailable) {
                    isEnd = true;
                    break;
                }
            }

            Draw_Color_block(qrImg, blockRow, blockCol, blockSize, colorIndex);
            filledBlocks++;

            if (blockRow % 2 == 1) {
                if (blockCol > 0) {
                    blockCol--;
                }
                else {
                    blockRow++;
                }
            }
            else {
                if (blockCol < col - 2) {
                    blockCol++;
                }
                else {
                    blockRow++;
                }
            }

            if (filledBits >= currCap && !isLastQR) {
                isEnd = true;
            }
            else if (blockRow >= row - 2) {
                isEnd = true;
            }
        }

        string outputDir = "BinQRImages";
        if (_access(outputDir.c_str(), 0) == -1) {
            if (_mkdir(outputDir.c_str()) != 0) {
                cout << "Error: Cannot create directory: " << outputDir << endl;
                return qrPaths;
            }
        }

        string qrPath = "BinQRImages/qrcode_" + to_string(qrCount) + ".jpg";
        bool saveOk = imwrite(qrPath, qrImg);
        if (saveOk) {
            cout << "Generated QR " << qrCount << ": " << qrPath << " (Bits: " << filledBits << ")" << endl;
            qrPaths.push_back(qrPath);
        }
        else {
            cout << "Error: Can't save " << qrPath << endl;
        }

        qrCount++;

        // 检查是否达到最大数据帧数限制
        if (qrCount >= maxDataFrames) {
            cout << "达到视频长度限制，停止生成更多QR码" << endl;
            cout << "已丢弃数据: " << (totalBinLen - currBinLoc) << " bits" << endl;
            break;
        }
    }

    fclose(binFp);

    cout << "总共生成 " << qrPaths.size() << " 个数据QR码" << endl;
    cout << "视频总帧数: " << (qrPaths.size() + 1) << " (1起始帧 + " << qrPaths.size() << "数据帧)" << endl;
    cout << "视频时长: " << ((qrPaths.size() + 1) * 1000 / fps) << "ms" << endl;

    return qrPaths;
}
bool encode::CreateVideoFromQRCodes(const vector<string>& qrPaths, int fps, const string& outputPath,
    int width, int height, int blockSize, int videoLengthMs) {
    if (qrPaths.empty()) {
        cout << "Error: No QR code images to create video." << endl;
        return false;
    }

    // 创建输出目录
    size_t lastSlash = outputPath.find_last_of("/\\");
    if (lastSlash != string::npos) {
        string outputDir = outputPath.substr(0, lastSlash);
        if (_access(outputDir.c_str(), 0) == -1) {
            if (_mkdir(outputDir.c_str()) != 0) {
                cout << "Error: Failed to create output directory: " << outputDir << endl;
                return false;
            }
        }
    }

    // 计算预期视频长度（毫秒）
    int expectedFrames = qrPaths.size() + 1; // 起始帧 + 所有二维码帧
    int expectedDurationMs = (expectedFrames * 1000) / fps;

    // 检查视频长度是否超过约束
    if (expectedDurationMs > videoLengthMs) {
        cout << "错误: 编码后的视频长度 " << expectedDurationMs << "ms 超过了约束 " << videoLengthMs << "ms" << endl;
        cout << "请减少二维码数量或提高帧率" << endl;
        return false;
    }

    VideoWriter video;
    int fourcc = VideoWriter::fourcc('m', 'p', '4', 'v');
    if (!video.open(outputPath, fourcc, fps, Size(width, height), true)) {
        cout << "Error: Could not open the output video file for write." << endl;
        return false;
    }

    // 创建起始帧（只写入1次）
    Mat startFrame = Mat::zeros(height, width, CV_8UC3);
    int col = width / blockSize;
    int row = height / blockSize;
    Draw_Template(startFrame, col, row, blockSize);

    // 填充010101...数据
    bool isEnd = false;
    int blockRow = 2;
    int blockCol = 2;
    bool alternate = false;

    auto isInPositionArea = [&](int r, int c) {
        const int POS_SIZE = 15;
        bool inTopLeft = (r >= 2 && r < 2 + POS_SIZE && c >= 2 && c < 2 + POS_SIZE);
        bool inTopRight = (r >= 2 && r < 2 + POS_SIZE && c >= col - 2 - POS_SIZE && c < col - 2);
        bool inBottomLeft = (r >= row - 2 - POS_SIZE && r < row - 2 && c >= 2 && c < 2 + POS_SIZE);
        bool inBottomRight = (r >= row - 10 && r < row - 2 && c >= col - 10 && c < col - 2);
        bool inFinder = inTopLeft || inTopRight || inBottomLeft || inBottomRight;
        bool inTopSyncOrLen = (r == 2) && (c >= 17 && c <= 32);
        bool inSafeArea = (r == 0) || (r == row - 1) || (c == 0) || (c == col - 1) ||
            (r == 1) || (r == row - 2) || (c == 1) || (c == col - 2);
        return inFinder || inTopSyncOrLen || inSafeArea;
        };

    while (!isEnd) {
        while (isInPositionArea(blockRow, blockCol)) {
            if (blockRow % 2 == 1) {
                if (blockCol > 0) blockCol--;
                else blockRow++;
            }
            else {
                if (blockCol < col - 2) blockCol++;
                else blockRow++;
            }
            if (blockRow >= row - 2) {
                isEnd = true;
                break;
            }
        }
        if (isEnd) break;

        int colorIndex = alternate ? 1 : 0;
        alternate = !alternate;

        Draw_Color_block(startFrame, blockRow, blockCol, blockSize, colorIndex);

        if (blockRow % 2 == 1) {
            if (blockCol > 0) blockCol--;
            else blockRow++;
        }
        else {
            if (blockCol < col - 2) blockCol++;
            else blockRow++;
        }
        if (blockRow >= row - 2) isEnd = true;
    }

    // 写入起始帧（1次）
    video.write(startFrame);

    // 写入正常的二维码帧（各1次）
    for (const string& qrPath : qrPaths) {
        Mat frame = imread(qrPath);
        if (frame.empty()) {
            cout << "Warning: Could not read frame: " << qrPath << ", skipping..." << endl;
            continue;
        }
        if (frame.size() != Size(width, height)) {
            resize(frame, frame, Size(width, height));
        }
        video.write(frame);
    }

    video.release();

    cout << "视频创建成功: " << outputPath << endl;
    cout << "实际视频长度: " << expectedDurationMs << "ms" << endl;
    cout << "总帧数: " << expectedFrames << " 帧" << endl;

    return true;
}